home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / comm / misc / xprz31.lha / XprZmodem / Send.c < prev    next >
C/C++ Source or Header  |  1993-08-17  |  17KB  |  644 lines

  1. /**********************************************************************
  2.  * Send.c: File transmission routines for xprzmodem.library;
  3.  * Original Version 2.10, 12 February 1991, by Rick Huebner.
  4.  * Based closely on Chuck Forsberg's sz.c example ZModem code,
  5.  * but too pervasively modified to even think of detailing the changes.
  6.  * Released to the Public Domain; do as you like with this code.
  7.  *
  8.  * Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
  9.  * Version 2.51 29, January 1992, RX_timout fix by John Tillema
  10.  * Version 2.52   6 March 1992, Very minor fix with compiled 020 library
  11.  *               by William M. Perkins.
  12.  * Version 2.63, 30 July 1993 build in locale, by Rainer Hess
  13.  * Version 3.1,  17 August 1993, added Auto-Blocksize by Rainer Hess
  14.  *
  15.  **********************************************************************/
  16.  
  17. #include "xprzmodem_all.h"
  18.  
  19. #define CATCOMP_NUMBERS
  20. #include "xprzmodem_catalog.h"
  21.  
  22. #ifdef DEBUGLOG
  23. extern void *DebugLog;
  24. #endif
  25.  
  26. /**********************************************************
  27.  *      long XProtocolSend(struct XPR_IO *xio)
  28.  *
  29.  * Main file transmission routine; called by comm program
  30.  **********************************************************/
  31. long __saveds __asm 
  32. XProtocolSend (register __a0 struct XPR_IO *xio)
  33. {
  34.   struct Vars *v;
  35.   short err;
  36.  
  37.   /* Perform common setup and initializations */
  38.   if (!(v = setup (xio)))
  39.     return XPRS_FAILURE;
  40.  
  41.   /*  was 600, set to 300 to fix so it uploads correctly */
  42.   v->Rxtimeout = 300;
  43.   v->Wantfcs32 = TRUE;
  44.   v->Rxflags = 0;
  45.  
  46.   /* Transfer the files */
  47.   zmputs (v, "rz\r");
  48.   stohdr (v, 0L);
  49.   zshhdr (v, ZRQINIT);
  50.   sendbuf (v);
  51.   if (getzrxinit (v) == ERROR)
  52.     upderr (v, GetLocalString( &li, MSG_UPLOAD_USER_ERROR ));
  53.   else
  54.     sendbatch (v);
  55.  
  56.   /* Clean up and return */
  57.   if (err = v->Errcnt)
  58.     upderr (v, GetLocalString( &li, MSG_SKIPPED_DUE_TO_ERRORS ));
  59.   else
  60.     updmsg (v, GetLocalString( &li, MSG_DONE ));
  61.   if (v->io.xpr_setserial && v->Oldstatus != -1)
  62.     (*v->io.xpr_setserial) (v->Oldstatus);
  63.   FreeMem (v->Filebuf, v->Filebufmax);
  64.   FreeMem (v, (long) sizeof (struct Vars));
  65.  
  66. #ifdef DEBUGLOG
  67.   if (DebugLog)
  68.     {
  69.       (*v->io.xpr_fclose) ((long) DebugLog);
  70.       DebugLog = NULL;
  71.     }
  72. #endif
  73.   return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
  74. }                /* End of long XProtocolSend() */
  75.  
  76. /**********************************************************
  77.  *      short getzrxinit(struct Vars *v)
  78.  *
  79.  * Negotiate with receiver to start a file transfer
  80.  **********************************************************/
  81. short
  82. getzrxinit (struct Vars *v)
  83. {
  84.   short n;
  85.  
  86.   for (n = v->ErrorLimit; --n >= 0;)
  87.     {
  88.       /* Check for abort from comm program */
  89.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
  90.     return ERROR;
  91.       switch (zgethdr (v))
  92.     {
  93.     case ZCHALLENGE:    /* Echo receiver's challenge number */
  94.       stohdr (v, v->Rxpos);
  95.       zshhdr (v, ZACK);
  96.       sendbuf (v);
  97.       continue;
  98.     case ZCOMMAND:        /* They didn't see our ZRQINIT; try again */
  99.       stohdr (v, 0L);
  100.       zshhdr (v, ZRQINIT);
  101.       sendbuf (v);
  102.       continue;
  103.     case ZRINIT:        /* Receiver ready; get transfer parameters */
  104.       v->Rxflags = 0xFF & v->Rxhdr[ZF0];
  105.       v->Txfcs32 = (v->Wantfcs32 && (v->Rxflags & CANFC32));
  106.       v->Rxbuflen = ((USHORT) v->Rxhdr[ZP1] << 8) | v->Rxhdr[ZP0];
  107. #ifdef DEBUGLOG
  108.       xprsprintf (v->Msgbuf, "Txfcs32=%ld Rxbuflen=%ld Tframlen=%ld\n",
  109.          (long) v->Txfcs32, (long) v->Rxbuflen, (long) v->Tframlen);
  110.       dlog (v, v->Msgbuf);
  111.       D (DEBUGINFO);
  112. #endif
  113.       /* Use shortest of the two side's max frame lengths */
  114.       if (v->Tframlen && (!v->Rxbuflen || v->Tframlen < v->Rxbuflen))
  115.         v->Rxbuflen = v->Tframlen;
  116. #ifdef DEBUGLOG
  117.       xprsprintf (v->Msgbuf, "Rxbuflen=%ld\n", (long) v->Rxbuflen);
  118.       dlog (v, v->Msgbuf);
  119.       D (DEBUGINFO);
  120. #endif
  121.       return OK;
  122.     case ZCAN:
  123.     case RCDO:
  124.     case TIMEOUT:
  125.       upderr (v, v->Msgbuf);
  126.       return ERROR;
  127.     case ZRQINIT:
  128.       if (v->Rxhdr[ZF0] == ZCOMMAND)
  129.         continue;
  130.       /* fallthrough... */
  131.     default:
  132.       zshhdr (v, ZNAK);
  133.       sendbuf (v);
  134.       continue;
  135.     }
  136.     }
  137.   return ERROR;
  138. }                /* End of short getzrxinit() */
  139.  
  140. /**********************************************************
  141.  *      void sendbatch(struct Vars *v)
  142.  *
  143.  * Send a batch of files
  144.  **********************************************************/
  145. void
  146. sendbatch (struct Vars *v)
  147. {
  148.   UBYTE single, done = FALSE;
  149.   long fstate;
  150.  
  151. #ifdef DEBUGLOG
  152.   D (DEBUGINFO);
  153. #endif
  154.  
  155.   /* If template routines not provided, must be single filename */
  156.   if (!v->io.xpr_ffirst || !v->io.xpr_fnext)
  157.     {
  158.       single = TRUE;
  159.       strcpy (v->Filename, v->io.xpr_filename);
  160.       /* Else use the template routines to get the first filename */
  161.     }
  162.   else
  163.     {
  164.       single = FALSE;
  165.       fstate = (*v->io.xpr_ffirst) (v->Filename, v->io.xpr_filename);
  166.       if (!fstate)
  167.     {
  168.       upderr (v, GetLocalString( &li, MSG_NO_FILES_MATCH_TEMPLATE ));
  169.       return;
  170.     }
  171.     }
  172.  
  173.   /* If using templates, keep getting names & sending until done */
  174.   while (!done)
  175.     {
  176.       if (sendone (v) == ERROR)
  177.     return;
  178.       if (single)
  179.     break;
  180.       fstate = (*v->io.xpr_fnext) (fstate, v->Filename, v->io.xpr_filename);
  181.       done = !fstate;
  182.     }
  183.  
  184.   /* End batch and return; if we never got started, just cancel receiver */
  185.   if (v->Filcnt)
  186.     saybibi (v);
  187.   else
  188.     canit (v);
  189. }                /* End of void sendbatch() */
  190.  
  191. /**********************************************************
  192.  *      short sendone(struct Vars *v)
  193.  *
  194.  * Send the file named in v->Filename
  195.  **********************************************************/
  196. short 
  197. sendone (struct Vars *v)
  198. {
  199.   struct SetupVars *sv;
  200.  
  201. #ifdef DEBUGLOG
  202.   xprsprintf (v->Msgbuf, "*** Sending %s\n", v->Filename);
  203.   dlog (v, v->Msgbuf);
  204.   D (DEBUGINFO);
  205. #endif
  206.  
  207.   /* Display name of file being sent for user */
  208.   v->xpru.xpru_updatemask = XPRU_FILENAME;
  209.   v->xpru.xpru_filename = v->Filename;
  210.   (*v->io.xpr_update) (&v->xpru);
  211.  
  212.   /* Set text/binary mode according to options before opening file */
  213.   set_textmode (v);
  214.  
  215.   /* Open the file, if possible */
  216.   if (!(v->File = bfopen (v, "r")))
  217.     {
  218.       ++v->Errcnt;
  219.       upderr (v, GetLocalString( &li, MSG_CANT_OPEN_FILE ));
  220.       return OK;        /* pass over it, there may be others */
  221.     }
  222.   ++v->Filcnt;
  223.   getsystime (&v->Starttime);
  224.  
  225.   /* Kick off the file transfer */
  226.   sv = (void *) v->io.xpr_data;
  227.   switch (sendname (v))
  228.     {
  229.     case ERROR:
  230.       ++v->Errcnt;
  231.       return ERROR;
  232.     case OK:
  233.       bfclose (v);
  234.       /* File sent; if option DY, delete file after sending */
  235.       if (*sv->option_d == 'Y' && v->io.xpr_extension >= 2 && v->io.xpr_unlink)
  236.     {
  237.       updmsg (v, GetLocalString( &li, MSG_DELETING_FILE_AFTER_SEND ));
  238.       (*v->io.xpr_unlink) (v->Filename);
  239.     }
  240.       break;
  241.     }
  242.   return OK;
  243. }                /* End of short sendone() */
  244.  
  245. /**********************************************************
  246.  *      short sendname(struct Vars *v)
  247.  *
  248.  * Build file info block consisting of file name, length,
  249.  * time, and mode
  250.  **********************************************************/
  251. short
  252. sendname (struct Vars *v)
  253. {
  254.   struct SetupVars *sv;
  255.   UBYTE *p, *q, buff[32];
  256.  
  257. #ifdef DEBUGLOG
  258.   D (DEBUGINFO);
  259. #endif
  260.  
  261.   /* Initialize comm program transfer status display */
  262.   v->Fsize = (v->io.xpr_finfo) ? (*v->io.xpr_finfo) (v->Filename, 1L) : -1;
  263.   v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
  264.     | XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
  265.     | XPRU_BYTES | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKSIZE;
  266.   v->xpru.xpru_protocol = "ZModem";
  267.   v->xpru.xpru_filesize = v->Fsize;
  268.   v->xpru.xpru_msg = (v->Lzconv == ZCNL) ? GetLocalString( &li, MSG_SENDING_TEXT_FILE ) :
  269.     ((v->Lzconv == ZCBIN) ? GetLocalString( &li, MSG_SENDING_BINARY_FILE ) : GetLocalString( &li, MSG_SENDING_FILE ));
  270.   v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
  271.   v->xpru.xpru_blockcheck = v->Crc32t ? "CRC-32" : "CRC-16";
  272.   v->xpru.xpru_bytes = v->Strtpos = 0;
  273.   v->xpru.xpru_blocksize = v->ksize;
  274.   update_rate (v);
  275.   (*v->io.xpr_update) (&v->xpru);
  276.  
  277.   sv = (void *) v->io.xpr_data;
  278.   if (*sv->option_s == 'Y')
  279.     {
  280.       /* If "SY" option selected, send full path */
  281.       strcpy (v->Pktbuf, v->Filename);
  282.       p = v->Pktbuf + strlen (v->Pktbuf) + 1;
  283.     }
  284.   else
  285.     {
  286.       /* else extract outgoing file name without directory path */
  287.       for (p = v->Filename, q = v->Pktbuf; *p; ++p, ++q)
  288.     if ((*q = *p) == '/' || *q == ':')
  289.       q = v->Pktbuf - 1;
  290.       *q = '\0';
  291.       p = ++q;
  292.     }
  293.  
  294.   /* Zero out remainder of file info packet */
  295.   memset (p, 0, sizeof (v->Pktbuf) - (p - v->Pktbuf));
  296.  
  297.   /* Store file size, timestamp, and mode in info packet */
  298.   /*
  299.      * XPR spec doesn't provide a way to get the file timestamp or file mode,
  300.      * so we'll just fake it with the current time and a dummy 0.
  301.    */
  302.   stcl_o (buff, getsystime (NULL) + UnixTimeOffset);
  303.   /* amiga.lib sprintf() can't do %lo format, so we do it the hard way */
  304.   /* Yes, octal; ZModem was originally done on Unix, and they like octal there */
  305.   xprsprintf (p, "%ld %s 0", (v->Fsize < 0) ? 0L : v->Fsize, buff);
  306.  
  307.   /* Send filename packet */
  308.   return zsendfile (v, (short) (p - v->Pktbuf + strlen (p) + 1));
  309. }                /* End of short sendname() */
  310.  
  311. /**********************************************************
  312.  *      short zsendfile(struct Vars *v, short blen)
  313.  *
  314.  * Send the filename packet and see if receiver will accept
  315.  * file
  316.  **********************************************************/
  317. short
  318. zsendfile (struct Vars *v, short blen)
  319. {
  320.   short c;
  321.  
  322. #ifdef DEBUGLOG
  323.   D (DEBUGINFO);
  324. #endif
  325.  
  326.   while (TRUE)
  327.     {
  328.       v->Txhdr[ZF0] = v->Lzconv;    /* Text or Binary mode; from config string */
  329.       v->Txhdr[ZF1] = LZMANAG;    /* Default file management mode */
  330.       v->Txhdr[ZF2] = LZTRANS;    /* Default file transport mode */
  331.       v->Txhdr[ZF3] = 0;
  332.       zsbhdr (v, ZFILE);
  333.       zsdata (v, blen, ZCRCW);
  334.       sendbuf (v);
  335.     again:
  336.       /* Check for abort from comm program */
  337.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
  338.     {
  339.       bfclose (v);
  340.       return ERROR;
  341.     }
  342.       switch (c = zgethdr (v))
  343.     {
  344.     case ZRINIT:
  345.       goto again;
  346.     case ZCAN:
  347.     case ZCRC:
  348.     case RCDO:
  349.     case TIMEOUT:
  350.     case ZABORT:
  351.     case ZFIN:
  352.       upderr (v, v->Msgbuf);
  353.       return ERROR;
  354.     case ZSKIP:        /* Receiver doesn't want this one */
  355.       upderr (v, GetLocalString( &li, MSG_SKIP_COMMAND_RECEIVED ));
  356.       bfclose (v);
  357.       return c;
  358.     case ZRPOS:        /* Receiver wants it; this is starting position */
  359.       bfseek (v, v->Rxpos);
  360.       v->Strtpos = v->Txpos = v->Rxpos;
  361.       if (v->io.xpr_sflush)
  362.         (*v->io.xpr_sflush) ();
  363.       v->Modemcount = 0;
  364.       return zsendfdata (v);
  365.     }
  366.     }
  367. }                /* End of short zsendfile() */
  368.  
  369. /**********************************************************
  370.  *      short zsendfdata(struct Vars *v)
  371.  *
  372.  * Send the file data
  373.  **********************************************************/
  374. short
  375. zsendfdata (struct Vars *v)
  376. {
  377.   short c, e, blklen, goodbytes = 0;
  378.   USHORT framelen, maxblklen, goodneeded = 512;
  379.  
  380. #ifdef DEBUGLOG
  381.   D (DEBUGINFO);
  382. #endif
  383.  
  384.   /* Figure out max data packet size to send */
  385.   maxblklen = v->ksize;
  386.   if (v->Rxbuflen && maxblklen > v->Rxbuflen)
  387.     maxblklen = v->Rxbuflen;
  388.   blklen = (v->Baud < 1200) ? 256 : v->ksize;
  389.   if (blklen > maxblklen)
  390.     blklen = maxblklen;
  391. #ifdef DEBUGLOG
  392.   xprsprintf (v->Msgbuf, "Rxbuflen=%ld blklen=%ld\n", (long) v->Rxbuflen,
  393.           (long) blklen);
  394.   dlog (v, v->Msgbuf);
  395.   D (DEBUGINFO);
  396. #endif
  397.  
  398.   /* If an interruption happened, handle it; else keep sending data */
  399. somemore:
  400.   while (char_avail (v))
  401.     {
  402.       /* Check for another incoming packet while discarding line noise */
  403.       switch (readock (v, 1))
  404.     {
  405.     case CAN:
  406.     case RCDO:
  407.     case ZPAD:
  408.       break;
  409.     default:
  410.       continue;
  411.     }
  412.     waitack:
  413. #ifdef DEBUGLOG
  414.       dlog (v, "--- At waitack\n");
  415.       D (DEBUGINFO);
  416. #endif
  417.       switch (c = getinsync (v))
  418.     {
  419.     default:
  420.       upderr (v, GetLocalString( &li, MSG_TRANSFER_CANCELLED ));
  421.       bfclose (v);
  422.       return ERROR;
  423.     case ZSKIP:        /* Receiver changed its mind and wants to skip the file */
  424.       return c;
  425.     case ZACK:        /* ACK at end of frame; resume sending data */
  426.       break;
  427.     case ZRPOS:        /* An error; resend data from last good point */
  428.       blklen >>= 2;
  429.       if (blklen < MINBLOCK)
  430.         blklen = MINBLOCK;
  431.       if (goodneeded < MAXGOODNEEDED)
  432.         goodneeded <<= 1;
  433.       v->xpru.xpru_updatemask = XPRU_ERRORS;
  434.       ++v->xpru.xpru_errors;
  435.       (*v->io.xpr_update) (&v->xpru);
  436.       break;
  437.     case ZRINIT:
  438.       updmsg (v, GetLocalString( &li, MSG_DONE ));
  439.       return OK;
  440.     }
  441.     }
  442.  
  443.   /* Transmit ZDATA frame header */
  444.   framelen = v->Rxbuflen;
  445.   stohdr (v, v->Txpos);
  446.   zsbhdr (v, ZDATA);
  447.  
  448.   /* Keep sending data packets until finished or interrupted */
  449.   do
  450.     {
  451.       /* Read next chunk of file data */
  452.       c = bfread (v, v->Pktbuf, (long) blklen);
  453.  
  454.       /* Figure out how to handle this data packet */
  455.       if (c < blklen)
  456.     e = ZCRCE;        /* If end of file, this is last data packet */
  457.       else if (v->Rxbuflen && (framelen -= c) <= 0)
  458.     e = ZCRCW;        /* If end of frame, ask for ACK */
  459.       else
  460.     e = ZCRCG;        /* Else tell receiver to expect more data packets */
  461.  
  462.       zsdata (v, c, e);        /* Send the packet */
  463.       sendbuf (v);
  464.  
  465.       /* Update comm program status display */
  466.       v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
  467.     | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE
  468.     | XPRU_BLOCKCHECK;
  469.       ++v->xpru.xpru_blocks;
  470.       v->xpru.xpru_blocksize = c;
  471.       v->xpru.xpru_blockcheck = v->Crc32t ? "CRC-32" : "CRC-16";
  472.       v->xpru.xpru_bytes = v->Txpos += c;
  473.       update_rate (v);
  474.       (*v->io.xpr_update) (&v->xpru);
  475.  
  476.       /*
  477.          * If we've been sending smaller than normal packets, see if it's
  478.          * time to bump the packet size up a notch yet
  479.        */
  480.       if (blklen < maxblklen && (goodbytes += c) >= goodneeded)
  481.     {
  482.       blklen <<= 1;
  483.       if (blklen > maxblklen)
  484.         blklen = maxblklen;
  485.       goodbytes = 0;
  486. #ifdef DEBUGLOG
  487.       xprsprintf (v->Msgbuf, "Bumping packet size to %ld at %ld\n",
  488.               (long) blklen, v->Txpos);
  489.       dlog (v, v->Msgbuf);
  490.       D (DEBUGINFO);
  491. #endif
  492.     }
  493.  
  494.       /* Give comm program its timeslice if it needs one */
  495.       if (v->io.xpr_chkmisc)
  496.     (*v->io.xpr_chkmisc) ();
  497.       /* Check for abort from comm program */
  498.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
  499.     goto aborted;
  500.       /* If this was last packet in frame, go wait for ACK from receiver */
  501.       if (e == ZCRCW)
  502.     goto waitack;
  503.  
  504.       /*
  505.          * Check if receiver trying to interrupt us; look for incoming packet
  506.          * while discarding line noise
  507.        */
  508.       while (char_avail (v))
  509.     {
  510.       switch (readock (v, 1))
  511.         {
  512.         case CAN:
  513.         case RCDO:
  514.         case ZPAD:
  515.           /* Interruption detected; stop sending and process complaint */
  516. #ifdef DEBUGLOG
  517.           dlog (v, "--- Interrupted send\n");
  518.           D (DEBUGINFO);
  519. #endif
  520.           zsdata (v, 0, ZCRCE);
  521.           sendbuf (v);
  522.           goto waitack;
  523.         }
  524.     }
  525.     }
  526.   while (e == ZCRCG);        /* If no interruption, keep sending data packets */
  527.  
  528.   /* Done sending file data; send EOF and wait for receiver to acknowledge */
  529.   while (TRUE)
  530.     {
  531.       updmsg (v, GetLocalString( &li, MSG_SENDING_EOF ));
  532.       stohdr (v, v->Txpos);
  533.       zsbhdr (v, ZEOF);
  534.       sendbuf (v);
  535.       switch (c = getinsync (v))
  536.     {
  537.     case ZACK:
  538.       continue;
  539.     case ZRPOS:
  540.       goto somemore;
  541.     case ZRINIT:
  542.       updmsg (v, GetLocalString( &li, MSG_EOF_ACKNOWLEDGED ));
  543.       ++v->Starttime.tv_secs;
  544.       update_rate (v);
  545.       v->xpru.xpru_updatemask = XPRU_EXPECTTIME | XPRU_ELAPSEDTIME
  546.         | XPRU_DATARATE;
  547.       (*v->io.xpr_update) (&v->xpru);
  548.       return OK;
  549.     case ZSKIP:
  550.       return c;
  551.     default:
  552.     aborted:
  553.       upderr (v, GetLocalString( &li, MSG_TRANSFER_CANCELLED ));
  554.       bfclose (v);
  555.       return ERROR;
  556.     }
  557.     }
  558. }                /* End of short zsendfdata() */
  559.  
  560. /**********************************************************
  561.  *      short getinsync(struct Vars *v)
  562.  *
  563.  * Respond to receiver's complaint, get back in sync with
  564.  * receiver
  565.  **********************************************************/
  566. short
  567. getinsync (struct Vars *v)
  568. {
  569.   short c;
  570.  
  571.   while (TRUE)
  572.     {
  573. #ifdef DEBUGLOG
  574.       dlog (v, "--- At getinsync\n");
  575.       D (DEBUGINFO);
  576. #endif
  577.       c = zgethdr (v);
  578.       if (v->io.xpr_sflush)
  579.     (*v->io.xpr_sflush) ();
  580.       v->Modemcount = 0;
  581.       switch (c)
  582.     {
  583.     case ZCAN:
  584.     case ZABORT:
  585.     case ZFIN:
  586.     case RCDO:
  587.     case TIMEOUT:
  588.       upderr (v, v->Msgbuf);
  589.       return ERROR;
  590.     case ZRPOS:
  591.       bfseek (v, v->Rxpos);
  592.       v->Txpos = v->Rxpos;
  593.       xprsprintf (v->Msgbuf, "%s %ld", GetLocalString( &li, MSG_RESENDING_FROM ), v->Txpos);
  594.       upderr (v, v->Msgbuf);
  595.       return c;
  596.     case ZSKIP:
  597.       upderr (v, GetLocalString( &li, MSG_SKIP_COMMAND_RECEIVED ));
  598.       /* fallthrough... */
  599.     case ZRINIT:
  600.       bfclose (v);
  601.       /* fallthrough... */
  602.     case ZACK:
  603.       return c;
  604.     default:
  605.       zsbhdr (v, ZNAK);
  606.       sendbuf (v);
  607.       continue;
  608.     }
  609.     }
  610. }                /* End of short getinsync() */
  611.  
  612. /**********************************************************
  613.  *      void saybibi(struct Vars *v)
  614.  *
  615.  * End of batch transmission; disengage cleanly from receiver
  616.  **********************************************************/
  617. void
  618. saybibi (struct Vars *v)
  619. {
  620. #ifdef DEBUGLOG
  621.   D (DEBUGINFO);
  622. #endif
  623.  
  624.   while (TRUE)
  625.     {
  626.       stohdr (v, 0L);
  627.       zsbhdr (v, ZFIN);
  628.       sendbuf (v);
  629.       switch (zgethdr (v))
  630.     {
  631.     case ZFIN:
  632.       sendline (v, 'O');
  633.       sendline (v, 'O');
  634.       sendbuf (v);
  635.       /* fallthrough... */
  636.     case ZCAN:
  637.     case RCDO:
  638.     case TIMEOUT:
  639.       return;
  640.     }
  641.     }
  642. }                /* End of void saybibi() */
  643. /* End of Send.c source */
  644.